package com.jorado.trace.util;


import brave.Span;
import brave.internal.HexCodec;
import brave.propagation.TraceContext;
import com.jorado.core.utility.NetUtils;
import com.jorado.core.utility.SnowFlake;
import com.jorado.core.utility.StringUtils;
import com.jorado.core.utility.UniqueIdUtils;
import com.jorado.json.JsonUtils;
import com.jorado.trace.ZipkinContext;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class TraceUtil {


    public static String getTraceId() {
        String requestId = RequestID.getRequestID();
        if (requestId == null || requestId.length() <= 0) {
            RequestID.setRequestID(UniqueIdUtils.generate());
        }
        return RequestID.getRequestID();
    }


    /**
     * 服务端接收 - http接口通过header
     *
     * @param params
     * @param header
     * @return
     */
    public static Map<String, String> serverReceiveByHttp(String service, String method, String url, Map<String, String> params, Map<String, List<String>> header) {
        // 对方appName
        String clientAppName = first(header.get("x-thriftproject"));
        String clientIp = first(header.get("x-thriftclient"));
        String traceId = first(header.get("x-b3-traceid"));
        if (StringUtils.isNotEmpty(traceId)) {
            RequestID.setRequestID(traceId);
        } else {
            traceId = UniqueIdUtils.generate();
            RequestID.setRequestID(traceId);
        }
        String spanId = first(header.get("x-b3-spanid"));
        String parentId = first(header.get("x-b3-parentspanid"));
        String sampled = first(header.get("x-b3-sampled"));
        if (!ZipkinContext.inst.isInit()) {
            ZipkinContext.startZipkin(1L, service + "." + method);
        }
        Map<String, String> logMap = TraceUtil.receive(service + "." + method, traceId, spanId, parentId, sampled);
        logMap.put("service", service);
        logMap.put("method", method);
        logMap.put("Uri", url);
        logMap.put("Params", JsonUtils.toJson(params));
        if (StringUtils.isNotEmpty(clientAppName)) {
            logMap.put("project", clientAppName);
        }
        logMap.put("type", "sr");
        return logMap;
    }


    /**
     * 服务端接收 - 内部map日志
     *
     * @param lastLogMap
     * @return
     */
    public static Map<String, String> serverReceiveByMap(String service, String method, Map<String, String> lastLogMap) {

        String traceId = lastLogMap.get("traceId");
        String spanId = lastLogMap.get("spanId");
        String parentId = lastLogMap.get("parentId");
        String sampled = lastLogMap.get("sampled");
        Map<String, String> logMap = TraceUtil.receive(service + "." + method, traceId, spanId, parentId, sampled);
        logMap.put("service", service);
        logMap.put("method", method);
        logMap.put("Params", lastLogMap.get("params"));
        logMap.put("startTime", System.currentTimeMillis() + "");
        logMap.put("type", "sr");
        return logMap;
    }

    /**
     * 服务端接收的公共方法
     *
     * @param name
     * @param traceId
     * @param spanIdStr
     * @param parentIdStr
     * @param sampledStr
     * @return
     */
    public static Map<String, String> receive(String name, String traceId, String spanIdStr, String parentIdStr, String sampledStr) {
        Map<String, String> logMap = new HashMap<String, String>();
        try {
            TraceContext traceCtx = null;
            String requestId = traceId;
            if (StringUtils.isEmpty(traceId)) {
                requestId = getTraceId();
            }
            long traceIdHigh = HexCodec.lowerHexToUnsignedLong(requestId.substring(0, 16));
            long traceIdLow = HexCodec.lowerHexToUnsignedLong(requestId.substring(16, 32));
            long spanId = 0L;
            long parentId = 0L;
            if (StringUtils.isNotEmpty(spanIdStr)) {
                spanId = HexCodec.lowerHexToUnsignedLong(spanIdStr);
            } else {
                spanId = SnowFlake.getInstance().nextId();
                spanIdStr = HexCodec.toLowerHex(spanId);
            }
            if (StringUtils.isNotEmpty(parentIdStr)) {
                parentId = HexCodec.lowerHexToUnsignedLong(parentIdStr);
            } else {
                parentId = 0;
            }
            boolean sampled = sampledStr != null ? sampledStr.equals("1") : true;
            traceCtx = TraceContext.newBuilder().traceId(traceIdLow).traceIdHigh(traceIdHigh)
                    .spanId(spanId).parentId(parentId).sampled(sampled).build();
            brave.Span client = ZipkinContext.inst.getTracing().tracer().toSpan(traceCtx).name(name).start();
            logMap.put("ServerType", "httpService");
            ZipkinContext.inst.getTracing().tracer().withSpanInScope(client);
            logMap.put("traceId", requestId);
            logMap.put("spanId", spanIdStr);
            logMap.put("parentId", parentIdStr);
            logMap.put("ipaddr", NetUtils.getIP());
            logMap.put("startTime", System.currentTimeMillis() + "");
            client.kind(Span.Kind.CLIENT);
            client.finish();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return logMap;
    }

    /**
     * 服务端-返回
     *
     * @param logMap
     * @return
     */
    public static Map<String, String> serverSend(Map<String, String> logMap, String status) {
        logMap.put("ServerType", "httpService");
        logMap.put("type", "ss");
        logMap.put("errorCode", status);
        if (logMap.containsKey("startTime")) {
            try {
                long startTime = Long.parseLong(logMap.get("startTime"));
                if (startTime > 0) {
                    logMap.put("ExecuteTime", (System.currentTimeMillis() - startTime) + "");
                }
            } catch (Exception e) {
            }
        }
        return logMap;
    }


    /**
     * 客户端 - 发送请求
     *
     * @param method
     * @return
     */
    public static Map<String, String> clientSend(String service, String method, String url, String params) {
        Map<String, String> logMap = new HashMap<String, String>();
        try {
            String requestId = getTraceId();
            if (ZipkinContext.inst.isInit()) {
                // 判断当前的span是否为空
                brave.Span current = TraceContextUtil.initTraceCtx(method);
                String spanId = HexCodec.toLowerHex(current.context().spanId());
                logMap.put("ServerType", "httpClient");

                logMap.put("Url", url);
                logMap.put("type", "cs");
                logMap.put("startTime", System.currentTimeMillis() + "");
                logMap.put("traceId", requestId);
                logMap.put("spanId", spanId);
                logMap.put("service", service);
                logMap.put("method", method);
                logMap.put("ipaddr", NetUtils.getIP());
                logMap.put("RequestBody", params);
                if (current.context().parentId() != null) {
                    String parentSpanId = HexCodec.toLowerHex(current.context().parentId());
                    logMap.put("parentSpanId", parentSpanId);
                }
                if (current != null) {
                    current.finish();
                }
            }
        } catch (Exception exp) {
            exp.printStackTrace();
            // 忽略异常
        }
        return logMap;
    }

    public static Map<String, String> clientSendNext(Map<String, String> logMap) {
        Map<String, String> logNewMap = new HashMap<>();
        logMap.entrySet().stream().forEach((entry) -> logNewMap.put(entry.getKey(), entry.getValue()));
        logNewMap.put("spanId", HexCodec.toLowerHex(SnowFlake.getInstance().nextId()));
        return logNewMap;
    }

    /**
     * 客户端-收到返回
     *
     * @param logMap
     * @return
     */
    public static Map<String, String> clientReceive(Map<String, String> logMap, String status) {
        logMap.put("ServerType", "httpClient");
        logMap.put("Status", status);
        logMap.put("type", "cr");
        if (logMap.containsKey("startTime")) {
            try {
                long startTime = Long.parseLong(logMap.get("startTime").toString());
                if (startTime > 0) {
                    logMap.put("ExecuteTime", (System.currentTimeMillis() - startTime) + "");
                }
            } catch (Exception e) {
            }
        }
        return logMap;
    }


    public static String first(List<String> list) {
        if (list != null && !list.isEmpty()) {
            return list.get(0);
        }
        return "";
    }
}
